#pragma once
#include"PageCache.h"

PageCache PageCache::_Inst;
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0);
	if (k > NPAGES - 1)
	{
		void* ptr = SystemAlloc(k);
		Span* span = _spanPool.New();
		span->_pageId= ((PAGE_ID)ptr >> PAGE_SHIFT);
		span->_n = k;
		_idSpanMap.set(span->_pageId,span);
		return span;
	}
	if (!_spanLists[k].Empty())
	{
		Span* kSpan=_spanLists[k].PopFront();
		for (PAGE_ID i = 0; i < kSpan->_n; i++)
		{
			_idSpanMap.set(kSpan->_pageId+i,kSpan);
		}
		return kSpan;
	}
	for (size_t i = k + 1; i < NPAGES; i++)
	{
		if (!_spanLists[i].Empty())
		{
			Span* nspan = _spanLists[i].PopFront();
			Span* kspan = _spanPool.New();

			kspan->_pageId = nspan->_pageId;
			kspan->_n = k;

			nspan->_pageId += k;
			nspan->_n-=k;

			_spanLists[nspan->_n].PushFront(nspan);
			_idSpanMap.set(nspan->_pageId,nspan);
			_idSpanMap.set(nspan->_pageId + nspan->_n- 1,nspan);
			
			for (PAGE_ID i = 0; i < kspan->_n; i++)
			{
				_idSpanMap.set(kspan->_pageId + i,kspan);
			}
			return kspan;
		}
	}
		Span* bigSpan = _spanPool.New();
		void* ptr=SystemAlloc(NPAGES-1);
		bigSpan->_pageId= ((PAGE_ID)ptr) >> PAGE_SHIFT;
		bigSpan->_n =NPAGES-1;
		_spanLists[bigSpan->_n].PushFront(bigSpan);
		return NewSpan(k);
}
Span* PageCache::MapObjectToSpan(void* obj)
{
	PAGE_ID id = ((PAGE_ID)obj >> PAGE_SHIFT);
	
	/*auto ret = _idSpanMap.find(id);
	if (ret != _idSpanMap.end())
	{
		return ret->second;
	}
	else
	{
		assert(false);
		return nullptr;
	}*/
	auto ret = (Span*)_idSpanMap.get(id);
	assert(ret != nullptr);
	return ret;
}
void PageCache::ReleaseSpanToPageCache(Span* span)
{
	if (span->_n > NPAGES - 1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		//delete span;
		_spanPool.Delete(span);

		return;
	}
	while (1)
	{
		PAGE_ID prevId = span->_pageId - 1;
		auto ret = (Span*)_idSpanMap.get(prevId);
		if (ret == nullptr)
		{
			break;
		}
		Span* prevSpan = ret;
		if (prevSpan->_isUse == true)
		{
			break;
		}
		if (prevSpan->_n+span->_n> 128)
		{
			break;
		}
		span->_pageId = prevSpan->_pageId;
		span->_n +=prevSpan->_n;

		_spanLists[prevSpan->_n].Erase(prevSpan);
		_spanPool.Delete(prevSpan);
	}
	while (1)
	{
		PAGE_ID nextId = span->_pageId + span->_n;
		auto ret = (Span*)_idSpanMap.get(nextId);
		if (ret == nullptr)
		{
			break;
		}
		Span* nextSpan = ret;
		if (nextSpan->_isUse == true)
		{
			break;
		}
		if (nextSpan->_n +span->_n> 128)
		{
			break;
		}
		span->_n += nextSpan->_n;
		_spanLists[nextSpan->_n].Erase(nextSpan);
		_spanPool.Delete(nextSpan);
	}

	_spanLists[span->_n].PushFront(span);
	span->_isUse = false;
	_idSpanMap.set(span->_pageId, span);
	_idSpanMap.set(span->_pageId + span->_n - 1,span);
}
